<?php

namespace BaseLing\WxXcxTool;

use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

abstract class WxToolsAbs
{
    /**
     * @param string $appid
     * @param string $secret
     * @param string $code
     * @return array
     * @throws GuzzleException
     * @throws Exception
     */
    public function code2session(string $appid, string $secret, string $code): array
    {
        $res = $this->client()->request('get',
            'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appid
            . '&secret=' . $secret
            . '&js_code=' . $code
            . '&grant_type=authorization_code');
        $resBody = $res->getBody()->getContents();
        if ($res->getStatusCode() === 200) {
            $resClass = json_decode($resBody, true);
            if (!isset($resClass['errcode']) && isset($resClass['session_key']) && isset($resClass['openid'])) {
                return $resClass;
            }
        }
        throw new Exception($resBody);
    }

    /**
     * @param string $appid
     * @param string $secret
     * @param string $code
     * @return array
     * @throws GuzzleException
     * @throws Exception
     */
    public function getPhoneByCode(string $appid, string $secret, string $code): array
    {
        $options = ['header' => ['Content-type: application/json'], 'body' => json_encode(['code' => $code])];
        $res = $this->client()->request('post',
            'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token='
            . $this->getToken($appid, $secret) . '&',
            $options);
        $resa = $res->getBody()->getContents();
        if (
            ($res->getStatusCode() === 200)
            && ($resClass = json_decode($resa, true))
            && ($resClass['errcode'] == 0)
            && $resClass['phone_info']) {
            return $resClass['phone_info'];
        } else {
            throw new Exception('获取手机号失败');
        }
    }

    public function getPhone(string $encryptedData, string $iv, string $sessionKey)
    {
//        $pc = new WxBizDataCrypt($this->wxAppId, $sessionKey);
//        $data = '';
//        $errCode = $pc->decryptData($encryptedData, $iv, $data);
//        if ($errCode === 0) {
//            return json_decode($data, true);
//        } else {
//            throw new BusinessException;
//        }
    }

    /**
     * @param string $appid 小程序appid
     * @param string $secret 小程序secret
     *
     * 接口说明地址地址： https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
     * @param array{
     *     page: string,
     *     miniprogram_state: string,
     *     tmpId: string,
     *     openid: string,
     *     data: array } $data
     * $data['page']: 点击模板卡片后的跳转页面，仅限本小程序内的页面
     * $data['miniprogram_state']: 跳转小程序类型：developer为开发版；trial为体验版；formal为正式版；默认为正式版
     * $data['tmpId']: 所需下发的订阅模板id
     * $data['openid']: 接收者（用户）的 openid
     * $data['data']: 模板内容，格式形如 { "key1": { "value": any }, "key2": { "value": any } }
     * @return string
     * @throws GuzzleException
     * @throws Exception
     */
    public function sendSubscribeMsg(string $appid, string $secret, array $data): string
    {
        $token = $this->getToken($appid, $secret);
        $options = [
            'header' => ['Content-type: application/json'],
            'body' => json_encode(['access_token' => $token,
                'page' => $data['page'] ?? '',
                'miniprogram_state' => $data['miniprogram_state'] ?? 'formal',
                'touser' => $data['openid'],
                'template_id' => $data['tmpId'],
                'data' => $data['data']],
            ),
        ];
        $res = $this->client()->request('post', 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=' . $token, $options);
        if ($res->getStatusCode() === 200) {
            return $res->getBody()->getContents();
        } else {
            throw new Exception('发送模板消息失败');
        }
    }

    /**
     * @throws GuzzleException
     * @throws Exception
     */
    public function getToken(string $appid, string $secret): string
    {
        if ($token = $this->getTokenCache()) {
            return $token;
        } else {
            $res = $this->client()->request('get', 'https://api.weixin.qq.com/cgi-bin/token?appid=' . $appid
                . '&secret=' . $secret
                . '&grant_type=client_credential');
            if (($res->getStatusCode() === 200)
                && ($resClass = json_decode($res->getBody()->getContents()))
                && $resClass->access_token
                && $resClass->expires_in) {
                $this->setTokenCache($resClass->access_token, $resClass->expires_in - 60);
                return $resClass->access_token;
            } else {
                throw new Exception('获取token失败');
            }
        }
    }

    abstract protected function client(): Client;

    abstract protected function getTokenCache(): string;

    abstract protected function setTokenCache(string $token, int $expires = 1000): string;
}
